// 判断点Q是否在p1和p2的线段上
function onSegment(p1: any, p2: any, q: any) {
  if (
    (q[0] - p1[0]) * (p2[1] - p1[1]) === (p2[0] - p1[0]) * (q[1] - p1[1]) &&
    Math.min(p1[0], p2[0]) <= q[0] &&
    q[0] <= Math.max(p1[0], p2[0]) &&
    Math.min(p1[1], p2[1]) <= q[1] &&
    q[1] <= Math.max(p1[1], p2[1])
  ) {
    return true
  }

  return false
}

/**
 * 判断点P在多边形内-射线法. Borrow from https://github.com/antvis/util/blob/master/packages/path-util/src/point-in-polygon.ts
 * @param points
 * @param x
 * @param y
 */
export const isPointInPolygon = (points: any, x: number, y: number) => {
  let isHit = false
  const n = points.length // 判断两个double在eps精度下的大小关系

  const tolerance = 1e-6

  function dcmp(xValue: any) {
    if (Math.abs(xValue) < tolerance) {
      return 0
    }

    return xValue < 0 ? -1 : 1
  }

  if (n <= 2) {
    // svg 中点小于 3 个时，不显示，也无法被拾取
    return false
  }

  for (let i = 0; i < n; i += 1) {
    const p1 = points[i]
    const p2 = points[(i + 1) % n]

    if (onSegment(p1, p2, [x, y])) {
      // 点在多边形一条边上
      return true
    } // 前一个判断min(p1[1],p2[1])<P.y<=max(p1[1],p2[1])
    // 后一个判断被测点 在 射线与边交点 的左边

    if (
      dcmp(p1[1] - y) > 0 !== dcmp(p2[1] - y) > 0 &&
      dcmp(x - ((y - p1[1]) * (p1[0] - p2[0])) / (p1[1] - p2[1]) - p1[0]) < 0
    ) {
      isHit = !isHit
    }
  }

  return isHit
}

/**
 * 是否在区间内
 * @param   {number}       value  值
 * @param   {number}       min    最小值
 * @param   {number}       max    最大值
 * @return  {boolean}      bool   布尔
 */
const isBetween = (value: number, min: number, max: number) => {
  return value >= min && value <= max
}

/**
 * 获取两条线段的交点
 * @param  {Point}  p0 第一条线段起点
 * @param  {Point}  p1 第一条线段终点
 * @param  {Point}  p2 第二条线段起点
 * @param  {Point}  p3 第二条线段终点
 * @return {Point}  交点
 */
export const getLineIntersect = (p0: any, p1: any, p2: any, p3: any) => {
  const tolerance = 0.0001
  const E = {
    x: p2.x - p0.x,
    y: p2.y - p0.y,
  }
  const D0 = {
    x: p1.x - p0.x,
    y: p1.y - p0.y,
  }
  const D1 = {
    x: p3.x - p2.x,
    y: p3.y - p2.y,
  }
  const kross = D0.x * D1.y - D0.y * D1.x
  const sqrKross = kross * kross
  const invertKross = 1 / kross
  const sqrLength0 = D0.x * D0.x + D0.y * D0.y
  const sqrLength1 = D1.x * D1.x + D1.y * D1.y

  if (sqrKross > tolerance * sqrLength0 * sqrLength1) {
    const s = (E.x * D1.y - E.y * D1.x) * invertKross
    const t = (E.x * D0.y - E.y * D0.x) * invertKross
    if (!isBetween(s, 0, 1) || !isBetween(t, 0, 1)) {
      return null
    }
    return {
      x: p0.x + s * D0.x,
      y: p0.y + s * D0.y,
    }
  }

  return null
}

// 判断两个BBox是否相交
export const intersectBBox = (box1: any, box2: any) => {
  return !(
    box2.minX > box1.maxX ||
    box2.maxX < box1.minX ||
    box2.minY > box1.maxY ||
    box2.maxY < box1.minY
  )
}

const lineIntersectPolygon = (lines: any, line: any) => {
  let isIntersect = false
  lines.every((l: any) => {
    if (getLineIntersect(l.from, l.to, line.from, line.to)) {
      isIntersect = true
      return false
    }
    return true
  })
  return isIntersect
}

const getBBox = (points: any) => {
  const xArray = points.map((p: any) => p[0])
  const yArray = points.map((p: any) => p[1])
  return {
    minX: Math.min.apply(null, xArray),
    maxX: Math.max.apply(null, xArray),
    minY: Math.min.apply(null, yArray),
    maxY: Math.max.apply(null, yArray),
  }
}

const parseToLines = (points: any) => {
  const lines = []
  const count = points.length

  for (let i = 0; i < count - 1; i += 1) {
    const point = points[i]
    const next = points[i + 1]
    lines.push({
      from: {
        x: point[0],
        y: point[1],
      },
      to: {
        x: next[0],
        y: next[1],
      },
    })
  }

  if (lines.length > 1) {
    const first = points[0]
    const last = points[count - 1]
    lines.push({
      from: {
        x: last[0],
        y: last[1],
      },
      to: {
        x: first[0],
        y: first[1],
      },
    })
  }

  return lines
} // 空数组，或者一个点返回 false

/**
 * 判断两个polygon是否相交。
 * borrow from @antv/path-util
 * @param points1 polygon1的顶点数组
 * @param points2 polygon2的顶点数组
 */

export const isPolygonsIntersect = (points1: any, points2: any) => {
  if (points1.length < 2 || points2.length < 2) {
    return false
  }

  const bbox1 = getBBox(points1)
  const bbox2 = getBBox(points2) // 判定包围盒是否相交，比判定点是否在多边形内要快的多，可以筛选掉大多数情况

  if (!intersectBBox(bbox1, bbox2)) {
    return false
  }

  let isIn = false // 判定点是否在多边形内部，一旦有一个点在另一个多边形内，则返回

  points2.every((point: any) => {
    if (isPointInPolygon(points1, point[0], point[1])) {
      isIn = true
      return false
    }
    return true
  })

  if (isIn) {
    return true
  }

  points1.every((point: any) => {
    if (isPointInPolygon(points2, point[0], point[1])) {
      isIn = true
      return false
    }
    return true
  })

  if (isIn) {
    return true
  }

  const lines1 = parseToLines(points1)
  const lines2 = parseToLines(points2)
  let isIntersect = false
  lines2.every((line: any) => {
    if (lineIntersectPolygon(lines1, line)) {
      isIntersect = true
      return false
    }
    return true
  })
  return isIntersect
}

export function pathToPoints(path: any) {
  const points: Array<any> = []
  path.forEach((seg: any) => {
    const command = seg[0]
    if (command !== 'A') {
      for (let i = 1; i < seg.length; i += 2) {
        points.push([seg[i], seg[i + 1]])
      }
    } else {
      const length1 = seg.length
      points.push([seg[length1 - 2], seg[length1 - 1]])
    }
  })

  return points
}

export const isItemIntersecPolygon = function isItemIntersecPolygon(
  item: any,
  polyPoints: any
) {
  let shapePoints
  const shape = item.getKeyShape()

  if (item.get('type') === 'path') {
    shapePoints = pathToPoints(shape.attr('path'))
  } else {
    const shapeBBox = shape.getCanvasBBox()
    shapePoints = [
      [shapeBBox.minX, shapeBBox.minY],
      [shapeBBox.maxX, shapeBBox.minY],
      [shapeBBox.maxX, shapeBBox.maxY],
      [shapeBBox.minX, shapeBBox.maxY],
    ]
  }

  return isPolygonsIntersect(polyPoints, shapePoints)
}
